import java.util.ArrayList;
import java.util.Collections;

public class TaskOrganizer {

	private ArrayList<PriorityTask> priorityList;
	private ArrayList<CalendarTask> calendarList;
	private StorageNew storage;

	public TaskOrganizer() {
		priorityList = new ArrayList<PriorityTask>();
		calendarList = new ArrayList<CalendarTask>();
		storage = new StorageNew();
		loadPrioTasks();
		loadCalTasks();
	}

	public void completeTimedTask(int i) throws Exception {
		i -= 1; // adjust index
		if (i < 0 || i >= calendarList.size()) {
			throw new Exception("Id must be in range");
		}
		calendarList.get(i).complete();
		storeCalTasks();
	}

	public void completeNontimedTask(int i) throws Exception {
		i -= 1; // adjust index
		if (i < 0 || i >= priorityList.size()) {
			throw new Exception("Id must be in range");
		}
		priorityList.get(i).complete();
		storePrioTasks();
	}

	public void clearCompletedTasks() {
		for (int i = 0; i < priorityList.size(); i++) {
			Task task = priorityList.get(i);
			if (task.isCompleted() == true) {
				priorityList.remove(task);
			}
		}
		for (int i = 0; i < calendarList.size(); i++) {
			Task task = calendarList.get(i);
			if (task.isCompleted() == true) {
				calendarList.remove(task);
			}
		}
		storeCalTasks();
		storePrioTasks();
		Collections.sort(priorityList);
		Collections.sort(calendarList);
	}

	public void addNontimedTask(String desc, int prio) throws Exception {
		if (prio > 3 || prio < 1) {
			throw new Exception("Prio must be between 1 and 3");
		}
		PriorityTask task = new PriorityTask(desc, prio);
		priorityList.add(task);
		Collections.sort(priorityList);
		storePrioTasks();
	}

	public void addTimedTask(String desc, int date, int time) throws Exception {
		if (!isValidDate(date)) {
			throw new Exception("Invalid date");
		}
		if (!isValidTime(time)) {
			throw new Exception("Invalid time");
		}
		CalendarTask task = new CalendarTask(desc, time, date);
		calendarList.add(task);
		Collections.sort(calendarList);
		storeCalTasks();
	}

	public void removeTimedTask(int id) throws Exception {
		id -= 1; // adjust index
		if (id < 0 || id >= calendarList.size()) {
			
			throw new Exception("Index out of range in calendar");
		}
		calendarList.remove(id);
		storeCalTasks();
	}

	public void removeNontimedTask(int id) throws Exception {
		id -= 1; // adjust index
		if (id < 0 || id >= priorityList.size()) {
			assert id > 0 && id < priorityList.size() : id;
			throw new Exception("Index out of range in ToDo-list");
		}
		priorityList.remove(id);
		storePrioTasks();
	}

	public void editNontimed(int id, String desc, int prio) throws Exception {
		id -= 1; // adjust index
		if (prio > 3 || prio < 1) {
			assert prio > 1 && prio < 3 : prio;
			throw new Exception("Prio must be between 1 and 3");
		}
		if (id < 0 || id >= priorityList.size()) {
			assert id > 0 && id < priorityList.size() : id;
			throw new Exception("Index out of range in ToDo-list");
		}
		PriorityTask task = priorityList.get(id);
		task.setDesc(desc);
		task.setPrio(prio);
		Collections.sort(priorityList);
		storePrioTasks();
	}

	public void editTimed(int id, String desc, int date, int time)
			throws Exception {
		id -= 1; // adjust index
		if (id < 0 || id >= calendarList.size()) {
			assert id > 0 && id < calendarList.size() : id;
			throw new Exception("Index out of range in calendar");
		}
		if (!isValidDate(date)) {
			assert isValidDate(date) == false;
			throw new Exception("Invalid date");
		}
		if (!isValidTime(time)) {
			assert isValidTime(time) == false;
			throw new Exception("Invalid time");
		}

		CalendarTask task = calendarList.get(id);
		task.setDesc(desc);
		task.setTime(time);
		task.setDate(date);
		Collections.sort(calendarList);
		storeCalTasks();
	}

	public String[][] getNontimedTasksOnKeywords(String keywords) {
		String[] words = keywords.split("\\+");
		ArrayList foundIndexesOfTasks = new ArrayList();

		for (int i = 0; i < priorityList.size(); i++) {
			PriorityTask task = priorityList.get(i);
			boolean foundTask = false;
			for (String word : words) {
				word = word.trim();
				String[] subwords = word.split("\\*");
				String desc = task.getDesc();
				String prio = String.valueOf(task.getPrio());
				if (isIn(desc, desc, prio, subwords)) {
					if (!foundTask) {
						foundIndexesOfTasks.add(i);
					}
					foundTask = true;
				}
			}
		}

		String[][] tasks = new String[foundIndexesOfTasks.size()][4];

		for (int i = 0; i < foundIndexesOfTasks.size(); i++) {
			int taskIndex = (Integer) foundIndexesOfTasks.get(i);
			PriorityTask task = priorityList.get(taskIndex);
			taskIndex += 1; // adjust index back
			tasks[i][0] = Integer.toString(taskIndex);
			tasks[i][1] = task.getDesc();
			tasks[i][2] = Integer.toString(task.getPrio());
			tasks[i][3] = Boolean.toString(task.isCompleted());
		}

		return tasks;
	}

	public String[][] getTimedTasksOnKeyWords(String keywords) {
		String[] words = keywords.split("\\+");
		ArrayList foundIndexesOfTasks = new ArrayList();

		for (int i = 0; i < calendarList.size(); i++) {
			CalendarTask task = calendarList.get(i);
			boolean foundTask = false;
			for (String word : words) {
				word = word.trim();
				String[] subwords = word.split("\\*");
				String desc = task.getDesc();
				String time = String.valueOf(task.getTime());
				String date = String.valueOf(task.getDate());
				if (isIn(desc, time, date, subwords)) {
					if (!foundTask) {
						foundIndexesOfTasks.add(i);
					}
					foundTask = true;
				}
			}
		}

		String[][] tasks = new String[foundIndexesOfTasks.size()][5];

		for (int i = 0; i < foundIndexesOfTasks.size(); i++) {
			int taskIndex = (Integer) foundIndexesOfTasks.get(i);
			CalendarTask task = calendarList.get(taskIndex);
			taskIndex += 1; // adjust index back
			tasks[i][0] = Integer.toString(taskIndex);
			tasks[i][1] = task.getDesc();
			tasks[i][2] = Integer.toString(task.getDate());
			tasks[i][3] = Integer.toString(task.getTime());
			tasks[i][4] = Boolean.toString(task.isCompleted());
		}

		return tasks;
	}

	private static boolean isIn(String a, String b, String c, String[] words) {
		for (String word : words) {
			word = word.trim();
			if (!a.contains(word) && !b.contains(word) && !c.contains(word)) {
				return false;
			}
		}
		return true;
	}

	public String[][] getNontimedTasks(int startPriority, int nrOfTasks)
			throws Exception {
		if (startPriority > 3 || startPriority < 1) {
			throw new Exception("Prio must be between 1 and 3");
		}
		if (nrOfTasks < -1) {
			throw new Exception("Invalid nr of tasks");
		}
		return getTasks(false, startPriority, nrOfTasks, priorityList);
	}

	public String[][] getTimedTasks(int startDate, int nrOfTasks)
			throws Exception {
		if (!isValidDate(startDate)) {
			throw new Exception("Invalid date");
		}
		if (nrOfTasks < -1) {
			throw new Exception("Invalid nr of tasks");
		}
		return getTasks(true, startDate, nrOfTasks, calendarList);
	}

	private String[][] getTasks(boolean timed, int startValue, int nrOfTasks,
			ArrayList list) {
		assert (startValue >= 0);
		assert (nrOfTasks > -1);

		int nrOfArgs;
		if (timed) {
			nrOfArgs = 5;
		} else {
			nrOfArgs = 4;
		}

		int startIndex = 0;
		boolean condition;
		for (int i = 0; i < list.size(); i++) {
			if (timed) {
				condition = (((CalendarTask) list.get(i)).getValue() >= startValue);
			} else {
				condition = (((PriorityTask) list.get(i)).getValue() <= startValue);
			}
			if (condition) {
				startIndex = i;
				break;
			}
		}

		// ///////////////
		if (nrOfTasks == -1) {
			nrOfTasks = list.size() - startIndex;
		}
		// //////////////

		String[][] tasks = new String[nrOfTasks][nrOfArgs];
		for (int i = 0; i < nrOfTasks; i++) {
			int index = startIndex + i;
			if ((startIndex + i) < list.size()) { // do not go out of range in
													// priorityList
				int taskIndex = index + 1; // adjust index back
				tasks[i][0] = Integer.toString(taskIndex); // convert index(id)
															// of task to String
				tasks[i][1] = ((Task) list.get(index)).getDesc(); // already a
																	// String
				if (timed) {
					int date = ((CalendarTask) list.get(index)).getDate();
					int t = ((CalendarTask) list.get(index)).getTime();
					tasks[i][2] = String.format(String.format("%%0%dd", 6),
							date);
					tasks[i][3] = String.format(String.format("%%0%dd", 4), t);
					Task task = (Task) list.get(index);
					tasks[i][4] = String.valueOf(task.isCompleted());
				} else {
					tasks[i][2] = Integer.toString(((PriorityTask) list
							.get(index)).getPrio()); // convert prio/date of
														// task to String
					Task task = (Task) list.get(index);
					tasks[i][3] = String.valueOf(task.isCompleted());
				}
			}
		}
		return tasks;
	}

	public boolean isValidTime(int t) {
		if (t < 0 || t > 2359) {
			return false;
		}
		Integer tInt = t;
		String time = tInt.toString();
		int length = time.length();
		String h = "-1";
		String m = "-1";
		if (length == 3) {
			h = time.substring(0, 1);
			m = time.substring(1, 3);
		} else if (length == 4) {
			h = time.substring(0, 2);
			m = time.substring(2, 4);
		}
		int hours = Integer.valueOf(h);
		int minutes = Integer.valueOf(m);
		if (hours > 23) {
			return false;
		}
		if (minutes > 59) {
			return false;
		}
		return true;
	}

	public boolean isValidDate(int d) {

		if (d < 00000101 || d > 99991231) {
			return false;
		}

		assert d > 00000101 && d < 99991231 : d;

		Integer dInt = d;
		String date = dInt.toString();
		int length = date.length();

		if (length != 8) {
			return false;
		}

		assert d == 8 : d;

		String year = date.substring(0, 4);
		String month = date.substring(4, 6);
		String day = date.substring(6, 8);
		int years = Integer.valueOf(year);
		int months = Integer.valueOf(month);
		int days = Integer.valueOf(day);
		int[] daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		if (isLeapYear(years)) {
			daysInMonth[1] = 29;
		}
		if (years > 2100) {
			return false;
		}
		assert years <= 2100 : years;
		if (months > 12) {
			return false;
		}
		assert months <= 12 : months;
		if (days > daysInMonth[months - 1]) {
			return false;
		}
		assert days <= daysInMonth[months - 1] : days;
		return true;
	}

	private boolean isLeapYear(int year) {
		if (year % 400 == 0) {
			assert year % 400 == 0;
			return true;
			// assert false;
		} else if (year % 100 == 0) {
			assert year % 400 != 100;
			return false;

		} else if (year % 4 == 0) {
			assert year % 4 == 0;
			return true;
		}
		return false;
	}

	private void loadPrioTasks() {
		String[][] tasks = storage.readFile("priorityList.txt");
		if (tasks != null) {
			for (String[] task : tasks) {
				String desc = task[1];
				int prio = Integer.parseInt(task[2]);
				PriorityTask atask = new PriorityTask(desc, prio);
				priorityList.add(atask);
				if (Boolean.valueOf(task[0])) {
					atask.complete();
				}
			}
			Collections.sort(priorityList);

		}
	}

	private void storePrioTasks() {
		String[][] tasks = new String[priorityList.size()][3];
		int cnt = 0;
		for (PriorityTask task : priorityList) {
			tasks[cnt][0] = String.valueOf(task.isCompleted());
			tasks[cnt][1] = String.valueOf(task.getDesc());
			tasks[cnt][2] = String.valueOf(task.getPrio());
			cnt++;
		}
		storage.writeFile(tasks, "priorityList.txt");
	}

	private void loadCalTasks() {
		String[][] tasks = storage.readFile("calendarList.txt");
		if (tasks != null) {
			for (String[] task : tasks) {
				String desc = task[1];
				int date = Integer.parseInt(task[2]);
				int time = Integer.parseInt(task[3]);
				CalendarTask atask = new CalendarTask(desc, time, date);
				calendarList.add(atask);
				if (Boolean.valueOf(task[0])) {
					atask.complete();
				}
			}
			Collections.sort(calendarList);
		}
	}

	private void storeCalTasks() {
		String[][] tasks = new String[calendarList.size()][4];
		int cnt = 0;
		for (CalendarTask task : calendarList) {
			tasks[cnt][0] = String.valueOf(task.isCompleted());
			tasks[cnt][1] = String.valueOf(task.getDesc());
			tasks[cnt][2] = String.valueOf(task.getDate());
			tasks[cnt][3] = String.valueOf(task.getTime());
			cnt++;
		}
		storage.writeFile(tasks, "calendarList.txt");
	}

}
